package main

import (
	"log"
	"net/http"
)

/*
在 Go 语言中，用于分析程序性能的概要文件有三种，分别是：CPU 概要文件（CPU Profile）、内存概要文件（Mem Profile）和阻塞概要文件（Block Profile）。

	对于CPU概要文件来说，其中的每一段独立的概要信息都记录着，在进行某一次采样的那个时刻，CPU 上正在执行的 Go 代码。

	对于内存概要文件，其中的每一段概要信息都记载着，在某个采样时刻，正在执行的 Go 代码以及堆内存的使用情况，这里包含已分配和已释放的字节数量和对象数量。

	对于阻塞概要文件，其中的每一段概要信息，都代表着 Go 程序中的一个 goroutine 阻塞事件。

	注意，在默认情况下，这些概要文件中的信息并不是普通的文本，它们都是以二进制的形式展现的。如果使用一个常规的文本编辑器查看它们的话，那么肯定会看到一堆“乱码”。

	这时就可以显现出go tool pprof这个工具的作用了。
		go tool pprof cpuprofile.out

怎样让程序对 CPU 概要信息进行采样？
	需要用到runtime/pprof包中的 API。更具体地说，在想让程序开始对 CPU 概要信息进行采样的时候，需要调用这个代码包中的StartCPUProfile函数，而在停止采样的时候则需要调用该包中的StopCPUProfile函数。

	runtime/pprof.StartCPUProfile函数（以下简称StartCPUProfile函数）在被调用的时候，先会去设定 CPU 概要信息的采样频率，并会在单独的 goroutine 中进行 CPU 概要信息的收集和输出。

	注意，StartCPUProfile函数设定的采样频率总是固定的，即：100赫兹。也就是说，每秒采样100次，或者说每10毫秒采样一次。

	StopCPUProfile函数也会调用runtime.SetCPUProfileRate函数，并把参数值（也就是采样频率）设为0。这会让针对 CPU 概要信息的采样工作停止。

怎样设定内存概要信息的采样频率？
	针对内存概要信息的采样会按照一定比例收集 Go 程序在运行期间的堆内存使用情况。设定内存概要信息采样频率的方法很简单，只要为runtime.MemProfileRate变量赋值即可。
	这个变量的含义是，平均每分配多少个字节，就对堆内存的使用情况进行一次采样。如果把该变量的值设为0，那么，Go 语言运行时系统就会完全停止对内存概要信息的采样。该变量的缺省值是512 KB，也就是512千字节。

	注意，如果你要设定这个采样频率，那么越早设定越好，并且只应该设定一次，否则就可能会对 Go 语言运行时系统的采样工作，造成不良影响。比如，只在main函数的开始处设定一次。

	想获取内存概要信息的时候，还需要调用runtime/pprof包中的WriteHeapProfile函数。该函数会把收集好的内存概要信息，写到我们指定的写入器中。

	注意，通过WriteHeapProfile函数得到的内存概要信息并不是实时的，它是一个快照，是在最近一次的内存垃圾收集工作完成时产生的。

怎样获取到阻塞概要信息？
	调用runtime包中的SetBlockProfileRate函数，即可对阻塞概要信息的采样频率进行设定。该函数有一个名叫rate的参数，它是int类型的。
	这个参数的含义是，只要发现一个阻塞事件的持续时间达到了多少个纳秒，就可以对其进行采样。如果这个参数的值小于或等于0，那么就意味着 Go 语言运行时系统将会完全停止对阻塞概要信息的采样。

	在runtime包中，还有一个名叫blockprofilerate的包级私有变量，它是uint64类型的。这个变量的含义是，只要发现一个阻塞事件的持续时间跨越了多少个 CPU 时钟周期，就可以对其进行采样。

	runtime包中的SetBlockProfileRate函数 VS runtime的包级私有变量blockprofilerate
	实际上，这两者的区别仅仅在于单位不同。runtime.SetBlockProfileRate函数会先对参数rate的值进行单位换算和必要的类型转换，然后，它会把换算结果用原子操作赋给blockprofilerate变量。
由于此变量的缺省值是0，所以 Go 语言运行时系统在默认情况下并不会记录任何在程序中发生的阻塞事件。

	当需要获取阻塞概要信息的时候，需要先调用runtime/pprof包中的Lookup函数并传入参数值"block"，从而得到一个*runtime/pprof.Profile类型的值（以下简称Profile值）。
在这之后，还需要调用这个Profile值的WriteTo方法，以驱使它把概要信息写进我们指定的写入器中。

	这个WriteTo方法有两个参数，一个参数就是我们刚刚提到的写入器，它是io.Writer类型的。而另一个参数则是代表了概要信息详细程度的int类型参数debug。

	debug参数主要的可选值有两个，即：0和1。当debug的值为0时，通过WriteTo方法写进写入器的概要信息仅会包含go tool pprof工具所需的内存地址，这些内存地址会以十六进制的形式展现出来。

	当该值为1时，相应的包名、函数名、源码文件路径、代码行号等信息就都会作为注释被加入进去。另外，debug为0时的概要信息，会经由 protocol buffers 转换为字节流。
而在debug为1的时候，WriteTo方法输出的这些概要信息就是可以读懂的普通文本了。

runtime/pprof.Lookup函数的正确调用方式是什么？
	runtime/pprof.Lookup函数（以下简称Lookup函数）的功能是，提供与给定的名称相对应的概要信息。这个概要信息会由一个Profile值代表。如果该函数返回了一个nil，那么就说明不存在与给定名称对应的概要信息。

	runtime/pprof包已经预先定义了 6 个概要名称。它们对应的概要信息收集方法和输出方法也都已经准备好了。
直接拿来使用就可以了。它们是：goroutine、heap、allocs、threadcreate、block和mutex。
	goroutine:
		当把"goroutine"传入Lookup函数的时候，该函数会利用相应的方法，收集到当前正在使用的所有 goroutine 的堆栈跟踪信息。注意，这样的收集会引起 Go 语言调度器的短暂停顿。
	heap:
		那么它就会收集与堆内存的分配和释放有关的采样信息。这实际上就是我们在前面讨论过的内存概要信息。
	allocs:
		操作会与heap非常的相似
	在这两种情况下，Lookup函数返回的Profile值也会极其相像。只不过，在这两种Profile值的WriteTo方法被调用时，它们输出的概要信息会有细微的差别，而且这仅仅体现在参数debug等于0的时候。
		"heap"会使得被输出的内存概要信息默认以“在用空间”（inuse_space）的视角呈现，而"allocs"对应的默认视角则是“已分配空间”（alloc_space）。
		在用空间:
			已经被分配但还未被释放的内存空间
		已分配空间:
			所有的内存分配信息都会被展现出来，无论这些内存空间在采样时是否已被释放。
		此外，无论是"heap"还是"allocs"，在我们调用Profile值的WriteTo方法的时候，只要赋予debug参数的值大于0，那么该方法输出内容的规格就会是相同的。

	threadcreate:
		会使Lookup函数去收集一些堆栈跟踪信息。这些堆栈跟踪信息中的每一个都会描绘出一个代码调用链，这些调用链上的代码都导致新的操作系统线程产生。
	block:
		因争用同步原语而被阻塞的那些代码的堆栈跟踪信息。
	mutex:
		曾经作为同步原语持有者的那些代码，它们的堆栈跟踪信息。
		同步原语：
			指的是存在于 Go 语言运行时系统内部的一种底层的同步工具，或者说一种同步机制。
			它是直接面向内存地址的，并以异步信号量和原子操作作为实现手段。已经熟知的通道、互斥锁、条件变量、”WaitGroup“，以及 Go 语言运行时系统本身，都会利用它来实现自己的功能。

如何为基于 HTTP 协议的网络服务添加性能分析接口？
	一般情况下只要在程序中导入net/http/pprof代码包就可以了:
		import _ "net/http/pprof"
	然后，启动网络服务并开始监听:
		log.Println(http.ListenAndServe("localhost:8082", nil))

	在运行这个程序之后，可以通过在网络浏览器中访问http://localhost:8082/debug/pprof这个地址看到一个简约的网页

	在/debug/pprof/这个 URL 路径下还有很多可用的子路径，通过点选网页中的链接就可以了解到。
像allocs、block、goroutine、heap、mutex、threadcreate这 6 个子路径，在底层其实都是通过Lookup函数来处理的。

	在网页上可以点击相关的指标，下载完成之后，利用 go tool pprof 工具查看
		go tool pprof Downloads/profile（下载的文件地址）

*/
import _ "net/http/pprof"

func main() {

	log.Println(http.ListenAndServe("localhost:8082", nil))

}
